﻿using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Outcoder.UserOptionsModel.UserOptions
{
	public class ObjectUserOption<TSetting> : UserOptionBase<TSetting>
	{
		/* TODO: [DV] Comment. */
		public ObjectUserOption(
			Func<string> titleFunc,
			string settingKey,
			Func<TSetting> defaultValueFunc)
			: base(titleFunc, settingKey, defaultValueFunc)
		{
			TemplateNameFunc = () => typeof(TSetting).Name;
		}

		public void SetDirty()
		{
			var writer = ReaderWriter as ICompositeObjectWriter;
			if (writer != null)
			{
				writer.SetDirty();
			}
		}
	}

	public class CommandOption : CommandOption<object>
	{
		public CommandOption(Func<string> titleFunc, ICommand command, 
			Func<CommandOption<object>, Task<SaveOptionResult>> saveAction = null, 
			Func<object> defaultValueFunc = null)
			: base(titleFunc, command, saveAction, defaultValueFunc)
		{
			/* Intentionally left blank. */
		}
	}

	public class CommandOption<TSetting> : IUserOption, IUserOptionReaderWriter, INotifyPropertyChanged
	{
		public CommandOption(Func<string> titleFunc, ICommand command, 
			Func<CommandOption<TSetting>, Task<SaveOptionResult>> saveAction = null, 
			Func<TSetting> defaultValueFunc = null)
		{
			TitleFunc = ArgumentValidator.AssertNotNull(titleFunc, "titleFunc");
			Command = ArgumentValidator.AssertNotNull(command, "command");

			DefaultValueFunc = defaultValueFunc;
			//ExecuteAction = ArgumentValidator.AssertNotNull(executeAction, "executeAction");
			SaveAction = saveAction;
		}

		public ICommand Command { get; private set; }

		public Func<string> TitleFunc { get; private set; }

		public string Title
		{
			get
			{
				return TitleFunc();
			}
		}

		public Func<TSetting> DefaultValueFunc { get; private set; }

		public object DefaultValue
		{
			get
			{
				return DefaultValueFunc != null ? (object)DefaultValueFunc() : null;
			}
		}

		public IUserOptionReaderWriter ReaderWriter
		{
			get
			{
				return this;
			}
			set
			{
			}
		}

		TSetting optionValue;

		public TSetting Value
		{
			get
			{
				return optionValue;
			}
			set
			{
				if (!Equals(optionValue, value))
				{
					optionValue = value;
					OnPropertyChanged();
				}
			}
		}

		//public Action ExecuteAction { get; private set; }
		//
		//public void Execute()
		//{
		//	ExecuteAction();
		//}

		/// <summary>
		/// Gets or sets the description func, which is used to retrieve 
		/// a description of the option that may be displayed to the user.
		/// </summary>
		/// <value>
		/// The description func.
		/// </value>
		public Func<string> DescriptionFunc { get; set; }

		public string Description 
		{
			get
			{
				return DescriptionFunc != null ? DescriptionFunc() : null;
			}
		}

		/* Unused. */
		public string SettingKey { get; set; }

		string templateName = "RelayOption";

		public string TemplateName
		{
			get
			{
				return templateName;
			}
			set
			{
				if (templateName != value)
				{
					templateName = value;
					OnPropertyChanged();
				}
			}
		}

		public Func<Task> RefreshAction { get; set; }

		public IUserOption UserOption
		{
			get
			{
				return this;
			}
		}

		bool dirty;
		
		public bool Dirty
		{
			get
			{
				return dirty;
			}
			protected set
			{
				if (dirty != value)
				{
					dirty = value;
					OnPropertyChanged();
				}
			}
		}

		public Func<CommandOption<TSetting>, Task<SaveOptionResult>> SaveAction { get; private set; }

		public async Task<SaveOptionResult> Save()
		{
			if (SaveAction != null)
			{
				return await SaveAction(this);
			}

			return new SaveOptionResult();
		}

		public async Task Refresh(bool reload = false)
		{
			OnPropertyChanged("Title");

			if (RefreshAction != null)
			{
				await RefreshAction();
			}
		}

		public Type SettingType
		{
			get
			{
				return null;
			}
		}

		string imagePath;

		public string ImagePath
		{
			get
			{
				return imagePath;
			}
			set
			{
				if (imagePath != value)
				{
					imagePath = value;
					OnPropertyChanged();
				}
			}
		}

		public event PropertyChangedEventHandler PropertyChanged;

		protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			PropertyChangedEventHandler handler = PropertyChanged;
			if (handler != null)
			{
				handler(this, new PropertyChangedEventArgs(propertyName));
			}
		}
	}
}